using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace LunoCode.Render
{
	class Terrain : BaseObject
	{
		private int WIDTH;
		private int HEIGHT;

		private VertexBuffer vertexBuffer;
		private IndexBuffer indexBuffer;

		private VertexDeclaration vertexDecalaration;

		private float[,] heightData;

		public Terrain()
			: this(new Vector3(-10000, -4000, 10000), new Vector3(400, 450, 400))
		{
			
		}

		public Terrain(Vector3 position, Vector3 scale)
		{
			this.Position = position;
			this.Scale = scale;
		}

		public void Init(GraphicsDevice device, Texture2D heightMap)
		{
			LoadHeightData(heightMap);
			SetUpTerrainVertices(device);
			SetUpTerrainIndices(device);

			vertexDecalaration = new VertexDeclaration(device, VertexPositionNormalTexture.VertexElements);

		}

		#region Load Height data
		public void SetUpTerrainVertices(GraphicsDevice device)
		{
			VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[WIDTH * HEIGHT];
			for (int x = 0; x < WIDTH; x++)
				for (int y = 0; y < HEIGHT; y++)
				{
					vertices[x + y*WIDTH].Position = new Vector3(x, y, heightData[x, y]);
					vertices[x + y*WIDTH].Normal = new Vector3(0, 0, 1);

					vertices[x + y*WIDTH].TextureCoordinate.X = x/(float) WIDTH;
					vertices[x + y*WIDTH].TextureCoordinate.Y = y/(float) HEIGHT;
				}
			for (int x = 1; x < WIDTH - 1; x++)
			{
				for (int y = 1; y < HEIGHT - 1; y++)
				{
					Vector3 normX = new Vector3((vertices[x - 1 + y * WIDTH].Position.Z - vertices[x + 1 + y * WIDTH].Position.Z) / 2, 0, 1);
					Vector3 normY = new Vector3(0, (vertices[x + (y - 1) * WIDTH].Position.Z - vertices[x + (y + 1) * WIDTH].Position.Z) / 2, 1);
					vertices[x + y * WIDTH].Normal = normX + normY;
					vertices[x + y * WIDTH].Normal.Normalize();
				}
			}


			vertexBuffer =
				new VertexBuffer(device, typeof(VertexPositionNormalTexture),
				                 VertexPositionNormalTexture.SizeInBytes * HEIGHT * WIDTH, BufferUsage.Points);

			vertexBuffer.SetData(vertices);
		}

		private void SetUpTerrainIndices(GraphicsDevice device)
		{
			short[] indices = new short[(WIDTH - 1) * (HEIGHT - 1) * 6];
			for (int x = 0; x < WIDTH - 1; x++)
			{
				for (int y = 0; y < HEIGHT - 1; y++)
				{
					indices[(x + y * (WIDTH - 1)) * 6] = (short)((x + 1) + (y + 1) * WIDTH);
					indices[(x + y * (WIDTH - 1)) * 6 + 1] = (short)((x + 1) + y * WIDTH);
					indices[(x + y * (WIDTH - 1)) * 6 + 2] = (short)(x + y * WIDTH);

					indices[(x + y * (WIDTH - 1)) * 6 + 3] = (short)((x + 1) + (y + 1) * WIDTH);
					indices[(x + y * (WIDTH - 1)) * 6 + 4] = (short)(x + y * WIDTH);
					indices[(x + y * (WIDTH - 1)) * 6 + 5] = (short)(x + (y + 1) * WIDTH);
				}
			}

			indexBuffer = new IndexBuffer(device, sizeof(short) * indices.Length, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
			indexBuffer.SetData(indices);
		}
		private void LoadHeightData(Texture2D heightMap)
		{
			float minimumHeight = 255;
			float maximumHeight = 0;

			WIDTH = heightMap.Width;
			HEIGHT = heightMap.Height;
			Color[] heightMapColors = new Color[WIDTH * HEIGHT];

			heightMap.GetData(heightMapColors);

			heightData = new float[WIDTH, HEIGHT];

			for (int x = 0; x < WIDTH; x++)
				for (int y = 0; y < HEIGHT; y++)
				{
					heightData[x, y] = heightMapColors[x + y * WIDTH].R;
					if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y];
					if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y];
				}

			for (int x = 0; x < WIDTH; x++)
				for (int y = 0; y < HEIGHT; y++)
					heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30;
		}
		#endregion

		#region draw
		public void Draw(GraphicsDevice device, Texture2D grassTexture, Effect effect, Matrix view, Matrix proj)
		{
			effect.CurrentTechnique = effect.Techniques[0];

			effect.Parameters["WvpXf"].SetValue(World * view * proj);
			effect.Parameters["WorldXf"].SetValue(World);
			Matrix wIT = Matrix.CreateScale(Scale)
								 * Matrix.CreateRotationX(-MathHelper.PiOver2)
								 * Matrix.CreateScale(Scale)
								 * Matrix.Invert(Matrix.CreateTranslation(Position));
			effect.Parameters["WorldITXf"].SetValue(wIT);
			effect.Parameters["ViewIXf"].SetValue(Matrix.Invert(view));
			effect.Parameters["ColorTexture"].SetValue(grassTexture);

			effect.Begin();
			foreach (EffectPass pass in effect.CurrentTechnique.Passes)
			{
				pass.Begin();

				device.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
				device.Indices = indexBuffer;
				device.VertexDeclaration = new VertexDeclaration(device, VertexPositionNormalTexture.VertexElements);

				device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, (WIDTH - 1) * (HEIGHT - 1) * 2);

				pass.End();
			}
			effect.End();
		}

		#endregion


	}
}